package com.wcs.exam.controller.auth.biz;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.tencentcloudapi.faceid.v20180301.models.DetectAuthResponse;
import com.tencentcloudapi.faceid.v20180301.models.DetectInfoText;
import com.tencentcloudapi.faceid.v20180301.models.GetDetectInfoEnhancedResponse;
import com.tencentcloudapi.faceid.v20180301.models.LivenessRecognitionResponse;
import com.wcs.exam.common.base.BaseBiz;
import com.wcs.exam.common.base.Result;
import com.wcs.exam.common.base.exception.BaseException;
import com.wcs.exam.common.cache.CacheRedis;
import com.wcs.exam.common.config.ThreadContext;
import com.wcs.exam.common.constant.RedisConstant;
import com.wcs.exam.common.enums.CertificationClientEnum;
import com.wcs.exam.common.enums.CertificationStatusEnum;
import com.wcs.exam.common.enums.FaceIdTypeEnum;
import com.wcs.exam.common.enums.StatusEnum;
import com.wcs.exam.common.faceid.FaceContrastUtil;
import com.wcs.exam.common.faceid.bean.FaceQueryRecordResult;
import com.wcs.exam.common.faceid.bean.GetfaceidReturn;
import com.wcs.exam.common.server.base.BaseWxBiz;
import com.wcs.exam.common.server.upload.Upload;
import com.wcs.exam.common.server.upload.UploadService;
import com.wcs.exam.common.util.BeanUtil;
import com.wcs.exam.controller.auth.req.*;
import com.wcs.exam.controller.auth.resp.*;
import com.wcs.exam.controller.biz.SysConfigCommonBiz;
import com.wcs.exam.controller.biz.resp.LoginConfigResp;
import com.wcs.exam.controller.biz.resp.SysConfigFaceResp;
import com.wcs.exam.dao.UsersDao;
import com.wcs.exam.dao.impl.mapper.entity.Users;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.experimental.Accessors;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

/**
 * Auth-用户信息
 *
 * @author wcs
 * @since 2024-07-06
 */
@Component
@RequiredArgsConstructor
public class AuthUsersBiz extends BaseBiz {

    @NotNull
    private final UploadService uploadService;
    @NotNull
    private final SysConfigCommonBiz sysConfigCommonBiz;

    @NotNull
    private final CacheRedis cacheRedis;

    @NotNull
    private final UsersDao dao;

    @NotNull
    private final BaseWxBiz baseWxBiz;

    public Result<AuthUsersResp> view() {
        Users users = dao.getById(ThreadContext.getUserId());
        if (users != null && users.getStatus().equals(StatusEnum.NORMAL.getCode())) {
            AuthUsersResp resp = BeanUtil.copyProperties(users, AuthUsersResp.class);
            return Result.success(resp);
        }
        return Result.error("用户不存在或被禁用");
    }

    public Result<String> edit(AuthUsersReq req) {
        Users users = BeanUtil.copyProperties(req, Users.class);
        users.setId(ThreadContext.getUserId());
        boolean result = dao.updateById(users);
        if (result) {
            return Result.success("操作成功");
        }
        return Result.error("操作失败");
    }

    public Result<String> wxBinding(AuthWxBindingReq req) throws WxErrorException {
        Users users = dao.getById(ThreadContext.getUserId());
        if (StringUtils.hasText(users.getUnionId()) || StringUtils.hasText(users.getOpenId())) {
            return Result.error("您已绑定微信，请勿重复绑定");
        }

        // 获取微信用户信息
        LoginConfigResp loginConfig = sysConfigCommonBiz.getSysConfig(LoginConfigResp.class);
        WxOAuth2UserInfo userInfo = baseWxBiz.getAuthInfo(loginConfig.getWxPcLoginAppId(), loginConfig.getWxPcLoginAppSecret(), req.getCode());
        Users usersRecord = dao.getByUnionIdOrOpenId(userInfo.getUnionId(), userInfo.getOpenid());
        if (ObjectUtil.isNotNull(usersRecord)) {
            return Result.error("该微信已绑定其他账号，请更换微信重新绑定");
        }

        // 更新用户信息
        Users newUser = new Users();
        newUser.setId(users.getId());
        newUser.setUnionId(userInfo.getUnionId());
        newUser.setOpenId(userInfo.getOpenid());
        dao.updateById(newUser);
        return Result.success("操作成功");
    }

    public Result<String> wxUnbind() {
        Users users = dao.getById(ThreadContext.getUserId());
        Users newUsers = new Users();
        newUsers.setId(users.getId());
        newUsers.setUnionId("");
        newUsers.setOpenId("");
        dao.updateById(newUsers);
        return Result.success("操作成功");
    }

    /**
     * 用户信息-强制实名认证检测
     *
     * @return 强制实名认证检测结果
     */
    public Result<Boolean> certificationCompulsory() {
        // 查询用户是否存在
        Users users = dao.getById(ThreadContext.getUserId());
        if (ObjectUtil.isNull(users)) {
            return Result.error("用户信息不存在");
        }

        // 是否已经实名认证完成
        if (CertificationStatusEnum.SUCCESS_AUTH.getCode().equals(users.getCertificationStatus())) {
            return Result.success(Boolean.FALSE);
        }
        // 获取人脸配置
        SysConfigFaceResp configFace = sysConfigCommonBiz.getFace();
        return Result.success(ObjectUtil.isNotNull(configFace) && Boolean.TRUE.equals(configFace.getFaceCertificationCompulsory()));
    }

    /**
     * 用户信息-实名认证次数
     *
     * @return 实名认证次数
     */
    public Result<AuthUsersCertificationCountResp> certificationCount() {
        // 获取用户信息
        Users users = dao.getById(ThreadContext.getUserId());
        if (ObjectUtil.isNull(users)) {
            return Result.error("用户信息不存在");
        }

        // 获取人脸配置
        SysConfigFaceResp configFace = sysConfigCommonBiz.getFace();

        AuthUsersCertificationCountResp resp = new AuthUsersCertificationCountResp();
        resp.setEnableCertification(configFace.getFaceEnable());
        if (Boolean.TRUE.equals(configFace.getFaceCertificationErrorEnable()) && configFace.getFaceCertificationErrorLimit() > 0) {
            // 开启错误限制
            int availableCount = configFace.getFaceCertificationErrorLimit() - users.getFaceErrorCount();
            resp.setAllowCertification(Boolean.TRUE.equals(configFace.getFaceEnable()) && availableCount > 0);
            resp.setTotalCount(configFace.getFaceCertificationErrorLimit());
            resp.setUseCount(users.getFaceErrorCount());
            resp.setAvailableCount(availableCount);
        } else {
            // 关闭错误限制
            resp.setTotalCount(0);
            resp.setUseCount(users.getFaceErrorCount());
            resp.setAvailableCount(0);
            resp.setAllowCertification(Boolean.TRUE.equals(configFace.getFaceEnable()));
        }

        // 返回剩余次数
        return Result.success(resp);
    }

    /**
     * 用户信息-app实名认证-参数获取
     *
     * @param req 请求参数
     * @return 实名参数
     */
    public Result<AuthUsersAppFaceParamsResp> getAppFaceParams(AuthUsersAppFaceParamsReq req) {
        // 查询用户是否存在
        Users user = dao.getById(ThreadContext.getUserId());
        if (ObjectUtil.isNull(user)) {
            return Result.error("用户信息不存在");
        }
        // 校验是否已经实名认证通过
        if (CertificationStatusEnum.SUCCESS_AUTH.getCode().equals(user.getCertificationStatus())) {
            return Result.error("已经完成实名认证，无需重复进行");
        }

        SysConfigFaceResp configFace = sysConfigCommonBiz.getFace();
        if (ObjectUtil.isNull(configFace)) {
            return Result.error("人脸识别配置不存在");
        }
        if (StrUtil.isBlank(configFace.getTencentFaceWbAppid()) || StrUtil.isBlank(configFace.getTencentFaceSDKLicence())) {
            return Result.error("人脸识别配置错误");
        }

        // 校验是否达到最大错误限制次数
        if (configFace.getFaceCertificationErrorEnable() && configFace.getFaceCertificationErrorLimit() > 0 && user.getFaceErrorCount() >= configFace.getFaceCertificationErrorLimit()) {
            return Result.error("实名认证达到最大错误限制次数【" + configFace.getFaceErrorLimit() + "】次！如需继续完成认证，请联系管理员！");
        }

        String nonce = IdUtil.simpleUUID();
        String orderNo = IdUtil.simpleUUID();

        //获取 Access Token
        String accessToken = FaceContrastUtil.accessToken(configFace.getTencentFaceWbAppid(), configFace.getTencentFaceWbAppidSecret());
        if (StrUtil.isBlank(accessToken)) {
            return Result.error("获取access_token失败！");
        }
        String apiTicket = FaceContrastUtil.apiTicket(configFace.getTencentFaceWbAppid(), "SIGN", null, accessToken);
        if (StrUtil.isBlank(apiTicket)) {
            return Result.error("获取Ticket失败！");
        }
        String sign = FaceContrastUtil.sign(configFace.getTencentFaceWbAppid(), String.valueOf(user.getId()), nonce, apiTicket);
        if (StrUtil.isBlank(sign)) {
            return Result.error("人脸对比参数不对，请联系管理员!");
        }

        // 获取FaceId
        GetfaceidReturn getfaceidReturn = FaceContrastUtil.getFaceId(configFace.getTencentFaceWbAppid(), orderNo, req.getRealName(), req.getIdCard(), String.valueOf(user.getId()), null, null, sign, nonce);
        if (ObjectUtil.isNull(getfaceidReturn)) {
            return Result.error("合作方后台上传身份信息，请联系管理员!");
        }
        if (!"0".equals(getfaceidReturn.getCode())) {
            return Result.error(getfaceidReturn.getMsg());
        }

        String nonceTicket = FaceContrastUtil.apiTicket(configFace.getTencentFaceWbAppid(), "NONCE", String.valueOf(user.getId()), accessToken);

        // 缓存实名认证参数，用于实名认证成功后保存
        cacheRedis.set(RedisConstant.Common.APP_CERTIFICATION + ThreadContext.getUserId() + RedisConstant.SEPARATOR + orderNo, JSONUtil.toJsonStr(req), 1, TimeUnit.HOURS);

        // 处理返回信息
        AuthUsersAppFaceParamsResp resp = new AuthUsersAppFaceParamsResp()
                .setUserId(user.getId())
                .setWbappid(configFace.getTencentFaceWbAppid())
                .setLicence(configFace.getTencentFaceSDKLicence())
                .setOrderNo(orderNo)
                .setNonce(nonce)
                .setSign(FaceContrastUtil.sign(configFace.getTencentFaceWbAppid(), String.valueOf(user.getId()), nonce, nonceTicket))
                .setFaceId(getfaceidReturn.getResult().getFaceId());
        return Result.success(resp);
    }

    public Result<AuthUsersDetectAuthUrlResp> detectAuthUrl(AuthUsersDetectAuthUrlReq req) {
        // 查询用户是否存在
        Users user = dao.getById(ThreadContext.getUserId());
        if (ObjectUtil.isNull(user)) {
            return Result.error("用户信息不存在");
        }
        // 校验是否已经实名认证通过
        if (CertificationStatusEnum.SUCCESS_AUTH.getCode().equals(user.getCertificationStatus())) {
            return Result.error("已经完成实名认证，无需重复进行");
        }

        SysConfigFaceResp configFace = sysConfigCommonBiz.getFace();
        if (ObjectUtil.isNull(configFace)) {
            return Result.error("人脸识别配置不存在");
        }
        if (StrUtil.isBlank(configFace.getLivenessRecognitionRuleId())) {
            return Result.error("人脸识别配置错误");
        }

        // 校验是否达到最大错误限制次数
        if (configFace.getFaceCertificationErrorEnable() && configFace.getFaceCertificationErrorLimit() > 0 && user.getFaceErrorCount() >= configFace.getFaceCertificationErrorLimit()) {
            return Result.error("实名认证达到最大错误限制次数【" + configFace.getFaceErrorLimit() + "】次！如需继续完成认证，请联系管理员！");
        }

        Long bizId = IdWorker.getId();
        JSONObject extraJson = new JSONObject();
        extraJson.set("bizId", bizId);

        DetectAuthResponse response = FaceContrastUtil.detectAuth(
                configFace.getTencentFaceSecretId(),
                configFace.getTencentFaceSecretKey(),
                configFace.getLivenessRecognitionRuleId(),
                req.getRealName(),
                req.getIdCard(),
                null,
                req.getRedirectUrl(),
                extraJson.toString());
        if (ObjectUtil.isNull(response)) {
            return Result.error("获取认证地址失败");
        }

        JSONObject detectAuthJson = new JSONObject();
        detectAuthJson.set("bizToken", response.getBizToken());
        detectAuthJson.set("ruleId", configFace.getLivenessRecognitionRuleId());
        detectAuthJson.set("realName", req.getRealName());
        detectAuthJson.set("idCard", req.getIdCard());

        // 因为返回的BizToken的有效期为7200秒，按1.5倍的时间进行存储，减少因为缓存过期导致的验证失败
        cacheRedis.set(RedisConstant.Common.USERS_DETECT_AUTH + ThreadContext.getUserId() + RedisConstant.SEPARATOR + bizId, detectAuthJson.toString(), 10800, TimeUnit.SECONDS);

        AuthUsersDetectAuthUrlResp resp = new AuthUsersDetectAuthUrlResp();
        resp.setUrl(response.getUrl());
        resp.setBizId(bizId);
        return Result.success(resp);
    }

    public Result<AuthUsersCertificationResultResp> certification(AuthUsersCertificationReq req) {
        // 查询用户是否存在
        Users user = dao.getById(ThreadContext.getUserId());
        if (ObjectUtil.isNull(user)) {
            return Result.error("用户信息不存在");
        }
        // 校验是否已经实名认证通过
        if (CertificationStatusEnum.SUCCESS_AUTH.getCode().equals(user.getCertificationStatus())) {
            return Result.error("已经完成实名认证，无需重复进行");
        }

        SysConfigFaceResp configFace = sysConfigCommonBiz.getFace();
        if (ObjectUtil.isNull(configFace)) {
            return Result.error("人脸识别配置不存在");
        }

        AuthUsersCertificationResultResp resp = new AuthUsersCertificationResultResp();
        resp.setCertificationPass(Boolean.FALSE);
        if (Boolean.TRUE.equals(configFace.getFaceCertificationErrorEnable()) && configFace.getFaceCertificationErrorLimit() > 0) {
            // 开启错误限制
            int availableCount = configFace.getFaceCertificationErrorLimit() - user.getFaceErrorCount();
            resp.setEnableCertification(Boolean.TRUE.equals(configFace.getFaceEnable()));
            resp.setAllowCertification(Boolean.TRUE.equals(configFace.getFaceEnable()) && availableCount > 0);
            resp.setTotalCount(configFace.getFaceCertificationErrorLimit());
            resp.setUseCount(user.getFaceErrorCount());
            resp.setAvailableCount(availableCount);
        } else {
            // 关闭错误限制
            resp.setEnableCertification(Boolean.TRUE.equals(configFace.getFaceEnable()));
            resp.setAllowCertification(Boolean.TRUE.equals(configFace.getFaceEnable()));
            resp.setTotalCount(0);
            resp.setUseCount(user.getFaceErrorCount());
            resp.setAvailableCount(0);
        }

        if (ObjectUtil.isNull(req.getCertificationClient())) {
            req.setCertificationClient(CertificationClientEnum.PC.getCode());
        }

        // 校验是否达到最大错误限制次数
        if (configFace.getFaceCertificationErrorEnable() && configFace.getFaceCertificationErrorLimit() > 0 && user.getFaceErrorCount() >= configFace.getFaceCertificationErrorLimit()) {
            resp.setResultMsg("实名认证达到最大错误限制次数【" + configFace.getFaceCertificationErrorLimit() + "】次！如需继续完成认证，请联系管理员！");
            return Result.success(resp);
        }

        // 获取实名认证结果
        FaceCheckResult checkResult = getFaceCheckResult(user.getIdCard(), user.getRealName(), req.getCertificationClient(), req.getContent(), configFace);

        // 判断是否达到最低相识度
        if (Boolean.TRUE.equals(checkResult.checkPass)) {
            // 更新人脸错误次数
            dao.lambdaUpdate()
                    .eq(Users::getId, user.getId())
                    .set(Users::getFaceErrorCount, user.getFaceErrorCount() + 1)
                    .update();
            resp.setUseCount(user.getFaceErrorCount() + 1);
            if (Boolean.TRUE.equals(configFace.getFaceCertificationErrorEnable()) && configFace.getFaceCertificationErrorLimit() > 0) {
                resp.setAvailableCount(resp.getTotalCount() - resp.getUseCount());
                resp.setAllowCertification(resp.getEnableCertification() && resp.getAvailableCount() > 0);
            }
            resp.setResultMsg("实名认证失败！");
        } else {
            // 上传最佳照片
            // 上传最佳照片
            String bestImage = updateImgBase64(checkResult.getBestFrameBase64());

            // 更新用户认证结果
            dao.lambdaUpdate()
                    .eq(Users::getId, user.getId())
                    .set(Users::getBestFrame, bestImage)
                    .set(Users::getFaceSim, checkResult.getSim())
                    .set(Users::getRealName, req.getRealName())
                    .set(Users::getIdCard, req.getIdCard())
                    .set(Users::getCertificationStatus, CertificationStatusEnum.SUCCESS_AUTH.getCode())
                    .update();
            resp.setCertificationPass(Boolean.TRUE);
            resp.setResultMsg(toI18nSuccess());
        }
        return Result.success(resp);
    }

    /**
     * 获取人脸识别结果
     *
     * @param idCard              用户信息
     * @param realName            用户信息
     * @param certificationClient 实名认证客户端
     * @param content             考勤内容
     * @param configFace          人脸识别配置
     * @return 人脸识别结果
     */
    private FaceCheckResult getFaceCheckResult(String idCard, String realName, Integer certificationClient, String content, SysConfigFaceResp configFace) {
        FaceCheckResult result = new FaceCheckResult()
                .setCheckPass(Boolean.FALSE)
                .setFaceIdType(FaceIdTypeEnum.LIVENESS_RECOGNITION.getCode())
                .setSim(BigDecimal.ZERO)
                .setRespMsg("");

        if (CertificationClientEnum.APP.getCode().equals(certificationClient)) {
            // APP端
            AuthUsersAppFaceParamsReq paramsReq = cacheRedis.get(RedisConstant.Common.APP_CERTIFICATION + ThreadContext.getUserId() + RedisConstant.SEPARATOR + content, AuthUsersAppFaceParamsReq.class);
            if (ObjectUtil.isNull(paramsReq)) {
                throw new BaseException("订单编号已失效");
            }
            result.setIdCard(paramsReq.getIdCard());
            result.setRealName(paramsReq.getRealName());

            FaceQueryRecordResult recordResult = FaceContrastUtil.queryFaceRecord(configFace.getTencentFaceWbAppid(), configFace.getTencentFaceWbAppidSecret(), content, "2");
            if (recordResult != null) {
                if (ObjectUtil.isNotNull(recordResult.getSimilarity())) {
                    BigDecimal sim = new BigDecimal(recordResult.getSimilarity());
                    result.setSim(sim);
                }
                if (StrUtil.isNotBlank(recordResult.getPhoto())) {
                    result.setBestFrameBase64(recordResult.getPhoto());
                }

                // 清空最佳照片，防止记录内容过大
                recordResult.setPhoto(null);
                recordResult.setVideo(null);
                result.setRespMsg(JSONUtil.toJsonStr(recordResult));
            }
        } else if (CertificationClientEnum.WECHAT_H5.getCode().equals(certificationClient)) {
            // 微信H5端
            String detectAuthStr = cacheRedis.get(RedisConstant.Common.USERS_DETECT_AUTH + ThreadContext.getUserId() + RedisConstant.SEPARATOR + content);
            if (StrUtil.isBlank(detectAuthStr)) {
                throw new BaseException("订单编号已失效");
            }
            JSONObject detectAuthJson = JSONUtil.parseObj(detectAuthStr);

            String bizToken = detectAuthJson.getStr("bizToken");
            String ruleId = detectAuthJson.getStr("ruleId");
            String userRealName = detectAuthJson.getStr("realName");
            String userIdCard = detectAuthJson.getStr("idCard");

            result.setRealName(userRealName);
            result.setIdCard(userIdCard);

            Integer faceIdType = detectAuthJson.getInt("faceIdType");
            if (ObjectUtil.isNotNull(faceIdType)) {
                result.setFaceIdType(faceIdType);
            }

            GetDetectInfoEnhancedResponse detectInfoEnhanced = FaceContrastUtil.getDetectInfoEnhanced(configFace.getTencentFaceSecretId(), configFace.getTencentFaceSecretKey(), ruleId, bizToken);
            if (detectInfoEnhanced != null) {
                DetectInfoText detectInfoText = detectInfoEnhanced.getText();
                if (ObjectUtil.isNotNull(detectInfoText)) {
                    if (StrUtil.isNotBlank(detectInfoText.getSim())) {
                        BigDecimal sim = new BigDecimal(detectInfoEnhanced.getText().getSim());
                        result.setSim(sim);
                    }
                    if (StrUtil.isNotBlank(detectInfoText.getExtra())) {
                        result.setExtra(detectInfoText.getExtra());
                    }
                }
                if (ObjectUtil.isNotNull(detectInfoEnhanced.getBestFrame()) && StrUtil.isNotBlank(detectInfoEnhanced.getBestFrame().getBestFrame())) {
                    result.setBestFrameBase64(detectInfoEnhanced.getBestFrame().getBestFrame());
                }

                // 清空最佳照片，防止记录内容过大
                detectInfoEnhanced.setBestFrame(null);
                detectInfoEnhanced.setEncryption(null);
                detectInfoEnhanced.setEncryptedBody(null);
                detectInfoEnhanced.setIdCardData(null);
                detectInfoEnhanced.setVideoData(null);
                result.setRespMsg(JSONUtil.toJsonStr(detectInfoEnhanced));
            }
        } else {
            // 其他
            LivenessRecognitionResponse recognitionResponse = FaceContrastUtil.livenessRecognition(configFace.getTencentFaceSecretId(), configFace.getTencentFaceSecretKey(), idCard, realName, content);
            if (recognitionResponse != null) {
                if (ObjectUtil.isNotNull(recognitionResponse.getSim())) {
                    result.setSim(BigDecimal.valueOf(recognitionResponse.getSim()));
                }
                if (StrUtil.isNotBlank(recognitionResponse.getBestFrameBase64())) {
                    result.setBestFrameBase64(recognitionResponse.getBestFrameBase64());
                }
                result.setIdCard(idCard);
                result.setRealName(realName);


                // 清空最佳照片，防止记录内容过大
                recognitionResponse.setBestFrameBase64(null);
                recognitionResponse.setBestFrameList(null);
                result.setRespMsg(JSONUtil.toJsonStr(recognitionResponse));
            }
        }

        result.setCheckPass(configFace.getFaceCertificationMinSim().compareTo(result.getSim()) <= 0);
        return result;
    }

    /**
     * 上传图片Base64字符串
     *
     * @param imgBase64 图片Base64字符串
     * @return 图片地址
     */
    private String updateImgBase64(String imgBase64) {
        // 上传最佳照片
        Upload upload = sysConfigCommonBiz.getUpload();
        return uploadService.uploadPicBase64(imgBase64, upload);
    }

    /**
     * 人脸识别结果
     */
    @Accessors(chain = true)
    @Data
    private static class FaceCheckResult implements Serializable {

        @Serial
        private static final long serialVersionUID = -1292920062767855734L;

        /**
         * 校验是否通过
         */
        private Boolean checkPass;

        /**
         * 人脸识别类型(1:活体人脸核身、2:活体人脸比对)
         */
        private Integer faceIdType;

        /**
         * 身份证号码
         */
        private String idCard;

        /**
         * 用户姓名
         */
        private String realName;

        /**
         * 相似度，取值范围 [0.00, 100.00]。推荐相似度大于等于70时可判断为同一人，可根据具体场景自行调整阈值（阈值70的误通过率为千分之一，阈值80的误通过率是万分之一）。
         */
        private BigDecimal sim;

        /**
         * 验证通过后的视频最佳截图照片，照片为BASE64编码后的值
         */
        private String bestFrameBase64;

        /**
         * 响应信息
         */
        private String respMsg;

        /**
         * 拓展信息（透传给人脸识别接口参数）
         */
        private String extra;
    }
}
